/*!	 compview.cpp
**	 writeme
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2008 Chris Moore
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
** === N O T E S ===========================================================
**
** ========================================================================= */

#ifdef USING_PCH
#	include "pch.h"
#else
#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

#include <synfig/general.h>

#include "compview.h"
#include "app.h"
#include <gtkmm/scrolledwindow.h>
#include <cassert>
#include <iostream>
#include "instance.h"
#include "canvasview.h"
#include <synfigapp/action.h>

#include <gui/localization.h>

#endif

#define ADD_TOOLBOX_BUTTON(button,stockid,tooltip)	\
	Gtk::Button *button = manage(new class Gtk::Button());	\
	button->add(*manage(new Gtk::Image(Gtk::StockID(stockid),Gtk::IconSize(4))));	\
	tooltip.set_tip(*button,tooltip);	\
	button->show_all()

using namespace std;
using namespace etl;
using namespace synfig;
using namespace studio;

#define COLUMNID_JUMP		(787584)
#define ColumnID	int

CompView::CompView():
    Gtk::Window(Gtk::WINDOW_TOPLEVEL),
    dialog_settings(this, "compview")
{
    assert(0); // CHECK: This class does not appear to be used.
    init_menu();
    set_type_hint(Gdk::WINDOW_TYPE_HINT_UTILITY);

    Gtk::Table *table = manage(new class Gtk::Table(2, 1, false));

    instance_selector.show();
    instance_selector.signal_changed().connect(sigc::mem_fun(this, &CompView::on_instance_selector_changed));

    table->attach(instance_selector, 0, 1, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 0, 0);

    notebook = manage(new class Gtk::Notebook());

    table->attach(*notebook, 0, 1, 1, 2, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, 0, 0);

    notebook->append_page(*create_canvas_tree(), _("Canvases"));
    notebook->append_page(*create_action_tree(), _("History"));

    signal_delete_event().connect(sigc::hide(sigc::mem_fun(*this, &CompView::close)));
    App::signal_instance_created().connect(sigc::mem_fun(*this, &studio::CompView::new_instance));
    App::signal_instance_deleted().connect(sigc::mem_fun(*this, &studio::CompView::delete_instance));
    App::signal_instance_selected().connect(sigc::mem_fun(*this, &studio::CompView::set_selected_instance_signal));

    table->show_all();
    add(*table);

    set_title(_("Canvas Browser"));
    set_modal(false);
    set_resizable(true);
    property_window_position().set_value(Gtk::WIN_POS_NONE);
    set_default_size(200, 300);
}

CompView::~CompView()
{
}

etl::loose_handle<studio::CanvasView>
CompView::get_selected_canvas_view()
{
    return get_selected_instance()->find_canvas_view(get_selected_canvas());
}

Gtk::Widget*
CompView::create_canvas_tree()
{
    studio::Instance::CanvasTreeModel canvas_tree_model;
    canvas_tree = manage(new class Gtk::TreeView());
    {
        Gtk::TreeView::Column* column = Gtk::manage(new Gtk::TreeView::Column(_("ID")));

        column->pack_start(canvas_tree_model.icon, false); // false = don't expand.
        column->pack_start(canvas_tree_model.label);

// #ifdef NDEBUG

        canvas_tree->append_column(*column);
    }
    canvas_tree->set_rules_hint();
    canvas_tree->signal_row_activated().connect(sigc::mem_fun(*this, &CompView::on_row_activate));
    canvas_tree->signal_event().connect(sigc::mem_fun(*this, &CompView::on_tree_event));
    canvas_tree->add_events(Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
    canvas_tree->add_events(Gdk::BUTTON1_MOTION_MASK);
    canvas_tree->show();

    Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
    scrolledwindow->set_can_focus(true);
    scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    scrolledwindow->add(*canvas_tree);
    scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
    scrolledwindow->show();

    return scrolledwindow;
}

Gtk::Widget*
CompView::create_action_tree()
{
    studio::HistoryTreeStore::Model history_tree_model;
    action_tree = manage(new class Gtk::TreeView());
    {
        Gtk::TreeView::Column* column = Gtk::manage(new Gtk::TreeView::Column(""));

        Gtk::CellRendererToggle* toggle_cr = Gtk::manage(new Gtk::CellRendererToggle());
        toggle_cr->signal_toggled().connect(sigc::mem_fun(*this, &studio::CompView::on_action_toggle));

        column->pack_start(*toggle_cr); // false = don't expand.
        column->add_attribute(toggle_cr->property_active(), history_tree_model.is_active);
        column->set_resizable();
        column->set_clickable();

        action_tree->append_column(*column);
    }

    {
        Gtk::TreeView::Column* column = Gtk::manage(new Gtk::TreeView::Column(_("Jump")));

        Gtk::CellRendererText* cell_renderer_jump = Gtk::manage(new Gtk::CellRendererText());
        column->pack_start(*cell_renderer_jump, true);

        cell_renderer_jump->property_text() = _("(JMP)");
        cell_renderer_jump->property_foreground() = "#003a7f";

        column->set_resizable();
        column->set_clickable();

        column->set_sort_column(COLUMNID_JUMP);

        action_tree->append_column(*column);
    }
    {
        Gtk::TreeView::Column* column = Gtk::manage(new Gtk::TreeView::Column(_("Action")));

        Gtk::CellRendererText *text_cr = Gtk::manage(new Gtk::CellRendererText());
        text_cr->property_foreground() = Glib::ustring("#7f7f7f");

        // column->pack_start(history_tree_model.icon, false); // false = don't expand.
        column->pack_start(*text_cr);
        column->add_attribute(text_cr->property_text(), history_tree_model.name);
        column->add_attribute(text_cr->property_foreground_set(), history_tree_model.is_redo);

        action_tree->append_column(*column);
    }

    action_tree->set_rules_hint();
    action_tree->signal_event().connect(sigc::mem_fun(*this, &CompView::on_action_event));
    action_tree->show();

    Gtk::ScrolledWindow *scrolledwindow = manage(new class Gtk::ScrolledWindow());
    scrolledwindow->set_can_focus(true);
    scrolledwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    scrolledwindow->add(*action_tree);
    scrolledwindow->set_shadow_type(Gtk::SHADOW_ETCHED_IN);
    scrolledwindow->show();

    Gtk::Button* clear_button(manage(new Gtk::Button(_("Clear Undo"))));
    clear_button->signal_pressed().connect(sigc::mem_fun(*this, &studio::CompView::clear_history));

    Gtk::Button* clear_redo_button(manage(new Gtk::Button(_("Clear Redo"))));
    clear_redo_button->signal_pressed().connect(sigc::mem_fun(*this, &studio::CompView::clear_redo));

    Gtk::Table* table(manage(new Gtk::Table()));
    table->attach(*scrolledwindow, 0, 2, 0, 1, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, 0, 0);
    table->attach(*clear_button, 0, 1, 1, 2, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 0, 0);
    table->attach(*clear_redo_button, 1, 2, 1, 2, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 0, 0);

    table->show_all();

    return table;
}

bool
CompView::close()
{
    hide();
    return true;
}

void
CompView::clear_history()
{

    if (selected_instance && App::dialog_message_b2(
                _("Clear History"),
                _("You will not be able to undo any changes that you have made! "
                  "Are you sure you want to clear the undo stack?")
                Gtk::MESSAGE_QUESTION,
                _("Cancel"),
                _("Clear"))
       ) {
        selected_instance->clear_undo_stack();
    }
}

void
CompView::clear_redo()
{
    if (selected_instance && App::dialog_message_2b(
                _("Clear History"),
                _("You will not be able to redo any changes that you have made! "
                  "Are you sure you want to clear the redo stack?")
                Gtk::MESSAGE_QUESTION,
                _("Cancel"),
                _("Clear"))
       ) {
        selected_instance->clear_redo_stack();
    }
}

void
CompView::init_menu()
{
    Gtk::MenuItem *item = NULL;

    item = manage(new Gtk::SeparatorMenuItem());
    item->show();
    menu.append(*item);

    item = manage(new Gtk::ImageMenuItem(Gtk::StockID("synfig-canvas_new")));
    item->signal_activate().connect(
        sigc::mem_fun(*this, &CompView::menu_new_canvas));
    item->show_all();
    menu.append(*item);

    item = manage(new Gtk::ImageMenuItem(Gtk::StockID("gtk-delete")));
    item->signal_activate().connect(
        sigc::mem_fun(*this, &CompView::menu_delete));
    item->show_all();
    menu.append(*item);

    item = manage(new Gtk::ImageMenuItem(Gtk::StockID("synfig-rename")));
    item->signal_activate().connect(
        sigc::mem_fun(*this, &CompView::menu_rename));
    item->show_all();
    menu.append(*item);
}

etl::loose_handle<synfig::Canvas>
CompView::get_selected_canvas()
{
    Glib::RefPtr<Gtk::TreeSelection> selection = canvas_tree->get_selection();

    if (!selection || !selection->get_selected()) {
        return 0;
    }

    studio::Instance::CanvasTreeModel canvas_tree_model;

    return static_cast<etl::handle<synfig::Canvas> >((*selection->get_selected())[canvas_tree_model.canvas]);
}

void
CompView::menu_new_canvas()
{
#warning Update Me!
}

void
CompView::menu_delete()
{
    studio::App::dialog_not_implemented();
}

void
CompView::menu_rename()
{
    studio::App::dialog_not_implemented();
}

void
CompView::set_selected_instance_signal(etl::handle<studio::Instance> x)
{
    set_selected_instance(x);
}

void
CompView::set_selected_instance_(etl::handle<studio::Instance> instance)
{
    if (studio::App::shutdown_in_progress) {
        return;
    }

    selected_instance = instance;

    if (instance) {
        canvas_tree->set_model(instance->canvas_tree_store());
        action_tree->set_model(instance->history_tree_store());
        canvas_tree->show();
        action_tree->show();
    } else {
        canvas_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
        action_tree->set_model(Glib::RefPtr< Gtk::TreeModel >());
        canvas_tree->hide();
        action_tree->hide();
    }
}

void
CompView::on_instance_selector_changed()
{
    int i = instance_selector.get_active_row_number();

    if (i < 0 || i >= (int)instances.size()) {
        return;
    }

    if (selected_instance == instances[i]) {
        return;
    }

    studio::App::set_selected_instance(instances[i]);
}

void
CompView::set_selected_instance(etl::loose_handle<studio::Instance> x)
{
    if (studio::App::shutdown_in_progress) {
        return;
    }

    // if it's already selected, don't select it again
    if (x == selected_instance) {
        return;
    }

    std::list<etl::handle<studio::Instance> >::iterator iter;

    if (x) {
        int i;

        for (i = 0, iter = studio::App::instance_list.begin(); iter != studio::App::instance_list.end() && ((*iter) != x); iter++, i++);

        assert(*iter == x);

        instance_selector.set_active(i);
    } else {
        instance_selector.set_active(0);
    }

    set_selected_instance_(x);
}

void
CompView::new_instance(etl::handle<studio::Instance> instance)
{
    if (studio::App::shutdown_in_progress) {
        return;
    }

    assert(instance);

    etl::loose_handle<studio::Instance> loose_instance(instance);

    instance->synfigapp::Instance::signal_filename_changed().connect(sigc::mem_fun(*this, &CompView::refresh_instances));
    instance->synfigapp::Instance::signal_filename_changed().connect(
        sigc::bind<etl::loose_handle<studio::Instance> >(
            sigc::mem_fun(*this, &CompView::set_selected_instance),
            loose_instance
        )
    );

    {
        std::string name = basename(instance->get_file_name());
        instance_selector.append(name);
        instances.push_back(loose_instance);
    }
}

void
CompView::delete_instance(etl::handle<studio::Instance> instance)
{
    if (studio::App::shutdown_in_progress) {
        return;
    }

    refresh_instances();

    if (selected_instance == instance) {
        set_selected_instance(0);
        instance_selector.set_active(0);
    }
}

void
CompView::refresh_instances()
{
    if (studio::App::shutdown_in_progress) {
        return;
    }

    instances.clear();
    instance_selector.set_active(-1);
    instance_selector.remove_all();

    std::list<etl::handle<studio::Instance> >::iterator iter;

    for (iter = studio::App::instance_list.begin(); iter != studio::App::instance_list.end(); iter++) {
        std::string name = basename((*iter)->get_file_name());
        instance_selector.append(name);
        instances.push_back(etl::loose_handle<studio::Instance>(*iter));
    }
}

void
CompView::on_row_activate(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *)
{
    assert(get_selected_instance());
    studio::Instance::CanvasTreeModel canvas_tree_model;
    const Gtk::TreeRow row = *(get_selected_instance()->canvas_tree_store()->get_iter(path));

    if (row[canvas_tree_model.is_canvas]) {
        get_selected_instance()->focus(row[canvas_tree_model.canvas]);
    } else {
        studio::App::dialog_not_implemented();
    }
}

bool
CompView::on_action_event(GdkEvent *event)
{
    studio::HistoryTreeStore::Model model;

    switch (event->type) {
    case GDK_BUTTON_PRESS:
    case GDK_2BUTTON_PRESS: {
        Gtk::TreeModel::Path path;
        Gtk::TreeViewColumn *column;
        int cell_x, cell_y;

        if (!action_tree->get_path_at_pos(
                    int(event->button.x), int(event->button.y),	// x, y
                    path, 				column, 				cell_x, cell_y // int&cell_x,int&cell_y
                )
           ) {
            break;
        }

        const Gtk::TreeRow row = *(action_tree->get_model()->get_iter(path));

        if ((ColumnID)column->get_sort_column_id() == COLUMNID_JUMP) {
            etl::handle<synfigapp::Action::Undoable> action(row[model.action]);

            if ((bool)row[model.is_undo]) {
                while (get_selected_instance()->undo_action_stack().size() && get_selected_instance()->undo_action_stack().front() != action) {
                    get_selected_instance()->undo();
                }
            } else if ((bool)row[model.is_redo]) {
                while (get_selected_instance()->redo_action_stack().size() && get_selected_instance()->undo_action_stack().front() != action) {
                    get_selected_instance()->redo();
                }
            }
        }

        break;
    }
    break;

    case GDK_BUTTON_RELEASE:
        break;

    default:
        break;
    }

    return false;
}

bool
CompView::on_tree_event(GdkEvent *event)
{
    switch (event->type) {
    case GDK_BUTTON_PRESS:
        switch (event->button.button) {
        case 3:
            if (get_selected_canvas()) {
                std::vector<Gtk::Widget*> children = menu.get_children();

                for (std::vector<Gtk::Widget*>::iterator i = children.begin(); i != children.end(); ++i) {
                    menu.remove(**i);
                }

                synfigapp::Action::ParamList param_list;
                param_list.add("canvas", synfig::Canvas::Handle(get_selected_canvas()));
                param_list.add("canvas_interface", get_selected_instance()->find_canvas_interface(get_selected_canvas()));
                get_selected_instance()->find_canvas_view(get_selected_canvas())->add_actions_to_menu(&menu, param_list, synfigapp::Action::CATEGORY_CANVAS);
                menu.popup(0, 0);
                menu.show();
            }

            break;

        default:
            break;
        }

        break;

    case GDK_MOTION_NOTIFY:
        break;

    case GDK_BUTTON_RELEASE:
        break;

    default:
        break;
    }

    return false;
}

void
CompView::on_action_toggle(const Glib::ustring& path_string)
{
    studio::HistoryTreeStore::Model history_tree_model;

    Gtk::TreePath path(path_string);

    const Gtk::TreeRow row = *(selected_instance->history_tree_store()->get_iter(path));

    handle<synfigapp::Action::Undoable> action = row[history_tree_model.action];

    selected_instance->synfigapp::Instance::set_action_status(action, !action->is_active());
}